import pathlib
from ..BaseCore.Selenium电商抽象类 import ShopOnline_CreateProduct
from ..BaseCore.Selenium电商抽象类 import WebDriver,WebElement,Service,Options,By,Keys
from ..BaseCore.Selenium电商抽象类 import abstractmethod
import json,typing


def group_by_key(
    items: typing.List[typing.Dict[str, typing.Any]],
    key_path: str, 
    sep: str = "."  # 路径分隔符，默认用.分隔嵌套层级
) -> typing.Dict[typing.Any, typing.List[typing.Dict[str, typing.Any]]]:
    """
    按指定键（支持嵌套路径）对字典列表进行分组，保持原始数据结构不变
    
    Args:
        items: 原始字典列表（支持嵌套字典结构）
        key_path: 分组依据的键路径（如单层键"price"，或嵌套键"parent.child"）
        sep: 路径分隔符，默认用.分隔（可自定义为其他符号如/）
    
    Returns:
        分组后的字典，键为路径对应的取值，值为包含原始字典的列表（数据结构不变）
    
    Raises:
        ValueError: 当键路径不存在或数据结构不匹配时抛出
    """
    groups = {}
    # 拆分路径为层级列表（如"a.b.c"拆分为["a", "b", "c"]）
    keys = key_path.split(sep)
    
    for item in items:
        current = item  # 用于逐层遍历嵌套结构
        try:
            # 按路径逐层获取值（处理嵌套）
            for key in keys:
                current = current[key]
            # 此时current就是路径对应的最终值（分组依据）
            group_key = current
        except (KeyError, TypeError) as e:
            # KeyError：路径中的某个键不存在
            # TypeError：中间节点不是字典（无法继续取值）
            raise ValueError(f"键路径 '{key_path}' 在元素 {item} 中无效：{str(e)}")
        
        # 按最终取值分组，保留原始item（数据结构不变）
        if group_key not in groups:
            groups[group_key] = []
        groups[group_key].append(item)
    
    return groups


class PinDuoDuo(ShopOnline_CreateProduct):
    def __init__(self):
        super().__init__()

    
    def LoadConfigFile(self, config_file = "config.json") -> typing.Self:
        """加载配置文件"""
        from ..BaseCore.ConfigManager import ConfigManager
        self.config = ConfigManager().load_config(config_file)
        self.fristcatgeory = self.config["pdd_fristcatgeory"]
        self.secondcatgeory = self.config["pdd_secondcatgeory"]
        self.thirdcatgeory = self.config["pdd_thirdcatgeory"]
        self.fourthcatgeory = self.config['pdd_fourthcatgeory']
        self.producttitle = self.config['pdd_producttitle']
        
        self.main_images = self.config.get("main_images",[])
        self.detail_images = self.config.get("detail_images",[])
        self.sku_images = self.config.get("sku_images",[])        
        self.main_video = self.config.get("main_video","")
        
        self.attributes = self.config.get("pdd_attributes",[])
        self.materials = self.config.get("pdd_materials",[])
        self.number = self.config.get("pdd_number",10000)
        
        self.sizes = self.config.get("pdd_sizes",{})
        self.size_chart = self.config.get("pdd_size_chart",[])
        return self


    def OpenCreateProductPage(self) -> typing.Self:
        """打开创建产品页面"""
        
        self.log.info("正在打开拼多多创建产品页面")
        
        _Create_Product_Page = "https://mms.pinduoduo.com/goods/category"
        self.driver.switch_to.new_window("tab")
        self.driver.get(url=_Create_Product_Page)
        
        self.log.info("已打开拼多多创建产品页面")
        
        return self


    def SelectFirstCategory(self) -> typing.Self:
        """选择一级分类"""
        if self.fristcatgeory:
            
            self.log.info(f"正在选择一级分类：{self.fristcatgeory}")
            
            self.finder.chain_find_element_safe(xpath=f"//div[@class='cate-container']//div[1]//*[text()='{self.fristcatgeory}']")
            self.mouse.chain_safe_click()
        return self


    def SelectSecondCategory(self) -> typing.Self:
        """选择二级分类"""
        if self.secondcatgeory:
            
            self.log.info(f"正在选择二级分类：{self.secondcatgeory}")
            
            self.finder.chain_find_element_safe(xpath=f"//div[@class='cate-container']//div[2]//*[text()='{self.secondcatgeory}']")
            self.mouse.chain_safe_click()
        return self


    def SelectThirdCategory(self) -> typing.Self:
        """选择三级分类"""
        if self.thirdcatgeory:
            
            self.log.info(f"正在选择三级分类：{self.thirdcatgeory}")
            
            self.finder.chain_find_element_safe(xpath=f"//div[@class='cate-container']//div[3]//*[text()='{self.thirdcatgeory}']")
            self.mouse.chain_safe_click()
        return self


    def SelectfourthCategory(self) -> typing.Self:
        """选择四级分类"""
        if self.fourthcatgeory:
            
            self.log.info(f"正在选择四级分类：{self.fourthcatgeory}")
            
            self.finder.chain_find_element_safe(xpath=f"//div[@class='cate-container']//div[4]//*[text()='{self.fourthcatgeory}']")
            self.mouse.chain_safe_click()
        return self


    def GotoParamSettingPage(self) -> typing.Self:
        """跳转到参数设置页面"""
        if self.finder.chain_find_element_safe(xpath="//button[./span[text()='确认发布该类商品']]").parent.current_element:
            
            self.log.info("正在跳转到参数设置页面!")
            
            self.base.wait(1)
            self.mouse.chain_safe_click()
        return self


    def FillProductTitle(self) -> typing.Self:
        """填写产品标题"""
        
        self.log.info(f"正在填写产品标题：{self.producttitle}")
        
        if self.finder.chain_by_text_find_input_element(text="商品标题",text_type="equal").parent.current_element:
            self.base.wait(5)
            self.finder.chain_find_element_safe(xpath=f"//input[@type='text'][contains(@placeholder,'商品标题')]")
            self.keyboard.chain_safe_input(InputValue=self.producttitle)
            
        return self


    def FillProductParams(self) -> typing.Self:
        """填写产品参数"""
        
        self.log.info("正在填写产品参数!")
        
        for attributeitem in self.attributes:
            attributeName = attributeitem["attribute"]
            attributeValue = attributeitem["value"]
            
            self.log.info(f"正在填写产品参数 [ {attributeName} ] [ {attributeValue} ]!")
            
            # # 打开选项
            self.finder.chain_find_element_safe(xpath=f"(//label[text()='商品属性']/following::div[@class='propertys-area']//*[text()='{attributeName}']/following::input)[1]")
            self.mouse.chain_safe_click()
            self.base.wait(1)
            

            if isinstance(attributeValue,list):
                for attributeValueitem in attributeValue:                    
                    self.finder.chain_find_element_safe(xpath=f"//ul[@role='listbox']/li//*[text()='{attributeValueitem}']").parent.mouse.chain_safe_click().parent.base.wait(1)
            else:
                self.keyboard.chain_only_clear().chain_only_input(InputValue=attributeValue)
                self.finder.chain_find_element_safe(xpath=f"//ul[@role='listbox']//li//*[text()='{attributeValue}']")
                self.mouse.chain_condition_click(condition_xpath=f"(//label[text()='商品属性']/following::*[text()='{attributeName}']/following::input[1]/following-sibling::*)[1]")
                self.base.wait(1)
        return self


    def FillProductMaterial(self) -> typing.Self:
        """填写产品材质"""
        
        self.log.info("正在填写产品材质!")
        
        return self


    def FillProductMainImage(self) -> typing.Self:
        """填写产品主图"""
        
        self.log.info("正在填写产品主图")
        self.log.info(f"正在上传图片 : {'\n'.join(self.main_images)}")
        self.finder.chain_find_element_safe(xpath=f"(//*[text()='基本信息']//following::input[@type='file'])[1]")
        self.keyboard.only_input(InputValue="\n".join(self.main_images))
        
        return self


    def FillProductDetailImage(self) -> typing.Self:
        """填写产品详情图"""
        
        self.log.info("正在填写产品详情图!")
        self.log.info(f"正在上传图片 : {'\n'.join(self.detail_images)}")
        self.finder.chain_find_element_safe(xpath=f"(//*[text()='快捷编辑']/following::input[@type='file'][contains(@accept,'image')])[1]")
        self.keyboard.only_input("\n".join(self.detail_images))
        
        return self


    def FillProductSku(self) -> typing.Self:
        """填写产品SKU"""
        
        self.log.info("正在填写产品SKU!")
        
        for index,skuitem in enumerate(self.sku_images):
            skuName = pathlib.Path(skuitem['path']).stem
            imagePath = skuitem['path']
            
            self.log.info(f"正在填写第 {index + 1} / {self.sku_images.__len__()} 个SKU [ {skuName} ]!")
            self.finder.chain_find_element_safe(xpath=f"(//label[text()='商品规格']/following::*[text()='颜色分类']/following::input[@type='text'][@placeholder='选择或输入主色'])[{index + 1}]")
            self.keyboard.chain_safe_input(skuName)
            self.finder.chain_find_element_safe(xpath=f"//input[@value='{skuName}']/following::input[@type='file'][1]")
            self.keyboard.chain_send_keys(InputValue=imagePath)


    def FillProductSizes(self) -> typing.Self:
        """填写产品规格"""
        
        self.log.info("正在填写产品规格!")
        
        sizeType = self.sizes['type']
        sizeValues = self.sizes['values']
        
        for index, sizeValue in enumerate(sizeValues):
            
            self.log.info(f"正在填写产品规格：{sizeValue}")
            
            self.finder.chain_find_element_safe(xpath=f"//div[@class='goods-sku-box']//div[@class='package-item-container']//div[text()='{sizeValue}']").parent.base.wait(1)
            self.mouse.chain_safe_click()
        return self
    
    def FillProductVideo(self) -> typing.Self:
        """填写产品视频"""
        if self.main_video:
            self.finder.chain_find_element_safe(
                xpath=f"(//*[text()='商品视频']/following::input[@type='file'][contains(@accept,'video')])[1]",
            ).parent.keyboard.only_input(InputValue=self.video_path)
        return self


    def FillProductPrice(self) -> typing.Self:
        """填写产品价格"""
        self.log.info("正在填写产品价格!")
        
        price_group = group_by_key(items=self.sku_images,key_path="price")
        
        for price in price_group:
            
            self.log.info(f"正在填写产品价格 : {price} 的商品")
            
            # 1.清除所有选中的颜色分类
            self.finder.chain_find_element_safe(xpath=f"(//label[text()='价格及库存']/following::div[@data-testid='beast-core-input'])[1]")
            self.mouse.chain_safe_click()
            self.finder.verify_element(xpath=f"//ul[@role='listbox']//li/span")
            
            while self.driver.find_elements(by=By.XPATH,value=f"(//ul[@role='listbox']//li[@data-checked='true']/span)"):
                e = self.driver.find_elements(by=By.XPATH,value=f"(//ul[@role='listbox']//li[@data-checked='true']/span)")[0]
                self.driver.execute_script("arguments[0].click();",e)
            
            # 2.选中同一价格的颜色分类
            for skuitem in price_group[price]:
                skuName = pathlib.Path(skuitem['path']).stem
                self.finder.chain_find_element_safe(xpath=f"//ul[@role='listbox']//li[@data-checked='false']/span[text()='{skuName}']").parent.mouse.chain_safe_click()

            # 3.填写价格            
            self.finder.chain_find_element_safe(xpath=f"//input[@placeholder='拼单价']").parent.keyboard.chain_safe_input(InputValue=price)
            self.finder.chain_find_element_safe(xpath=f"//input[@placeholder='单买价']").parent.keyboard.chain_safe_input(InputValue=float(price) + 20)
            self.finder.chain_find_element_safe(xpath=f"//button/span[text()='批量设置']").parent.mouse.chain_safe_click()
            self.base.wait(2)
            self.finder.chain_find_element_safe(xpath=f"//input[@placeholder='拼单价']").parent.keyboard.only_clear()
            self.finder.chain_find_element_safe(xpath=f"//input[@placeholder='单买价']").parent.keyboard.only_clear()

        return self


    def FillProductInventory(self) -> typing.Self:
        """填写产品库存"""
        
        self.log.info(f"正在填写产品库存：{self.number}")
        
        self.finder.chain_find_element_safe(xpath=f"(//label[text()='价格及库存']/following::div[@data-testid='beast-core-input'])[1]")
        self.mouse.chain_safe_click()
        self.finder.verify_element(xpath=f"//ul[@role='listbox']//li/span")
        while self.driver.find_elements(by=By.XPATH,value=f"(//ul[@role='listbox']//li[@data-checked='false']/span)"):
            e = self.driver.find_elements(by=By.XPATH,value=f"(//ul[@role='listbox']//li[@data-checked='false']/span)")[0]
            self.driver.execute_script("arguments[0].click();",e)
                
        self.finder.chain_find_element_safe(xpath=f"//input[@placeholder='库存']").parent.keyboard.chain_safe_input(InputValue=self.number)
        self.finder.chain_find_element_safe(xpath=f"//button/span[text()='批量设置']").parent.mouse.chain_safe_click()
        self.base.wait(2)
        return self


    def FillProductShopLabel(self) -> typing.Self:
        """填写产品商家编号"""
        
        self.log.info(f"正在填写产品商家编号")
        
        code_group = group_by_key(items=self.sku_images,key_path="code")
        
        for code in code_group:
            
            self.log.info(f"正在填写产品商家编号 : {code} 的商品")
            
            # 1.清除所有选中的颜色分类
            self.finder.chain_find_element_safe(xpath=f"(//label[text()='价格及库存']/following::div[@data-testid='beast-core-input'])[1]")
            self.mouse.chain_safe_click()
            self.finder.verify_element(xpath=f"//ul[@role='listbox']//li/span")
            
            while self.driver.find_elements(by=By.XPATH,value=f"(//ul[@role='listbox']//li[@data-checked='true']/span)"):
                e = self.driver.find_elements(by=By.XPATH,value=f"(//ul[@role='listbox']//li[@data-checked='true']/span)")[0]
                self.driver.execute_script("arguments[0].click();",e)
            
            # 2.选中同一编号的分类
            for skuitem in code_group[code]:
                skuName = pathlib.Path(skuitem['path']).stem
                self.finder.chain_find_element_safe(xpath=f"//ul[@role='listbox']//li[@data-checked='false']/span[text()='{skuName}']").parent.mouse.chain_safe_click()

            # 3.填写价格            
            self.finder.chain_find_element_safe(xpath=f"//input[@placeholder='规格编码']").parent.keyboard.chain_safe_input(InputValue=code)
            self.finder.chain_find_element_safe(xpath=f"//button/span[text()='批量设置']").parent.mouse.chain_safe_click()
            self.base.wait(2)
            self.finder.chain_find_element_safe(xpath=f"//input[@placeholder='规格编码']").parent.keyboard.only_clear()
        return self


    def FillProductLogistics(self) -> typing.Self:
        """填写产品物流信息"""
        
        self.log.info(f"正在填写产品物流信息")
        
        self.finder.chain_find_element_safe(xpath=f"//*[text()='服务与承诺']/following::*[text()='48小时发货及揽收']").parent.mouse.chain_safe_click().parent.base.wait(1)
        
        if self.finder.verify_element(xpath=f"//*[text()='服务与承诺']/following::*[text()='展开修改']"):
            self.finder.chain_find_element_safe(xpath=f"//*[text()='服务与承诺']/following::*[text()='展开修改']").parent.mouse.chain_safe_click().parent.base.wait(1)
        
        self.finder.chain_find_element_safe(xpath=f"//*[text()='服务与承诺']/following::*[text()='其他模板']").parent.mouse.chain_safe_click().parent.base.wait(1)
        self.finder.chain_find_element_safe(xpath=f"//*[text()='其他模板']/following::input[@readonly]").parent.mouse.chain_safe_click().parent.base.wait(1)
        self.finder.chain_find_element_safe(xpath=f"//ul[@role='listbox']//li//span[text()='新疆西藏不配送默认模板']").parent.mouse.chain_safe_click().parent.base.wait(1)
        
        
        return self

    def FillSizeCharts(self) -> typing.Self:
        """填写产品尺码表"""
        
        self.log.info(f"正在填写产品尺码表")
        
        self.finder.chain_find_element_safe(xpath=f"//*[text()='规格与库存']/following::*[text()='尺码表']/following::div[text()='身高(cm)']/ancestor::*[1]//input[@type='checkbox']")
        self.mouse.chain_safe_click()
        self.finder.chain_find_element_safe(xpath=f"//*[text()='规格与库存']/following::*[text()='尺码表']/following::div[text()='体重(kg)']/ancestor::*[1]//input[@type='checkbox']")
        self.mouse.chain_safe_click()
        self.base.wait(2)
        
        for _ in self.driver.find_elements(by=By.XPATH,value=f"//table//*[text()='区间值']"):
            self.driver.execute_script("arguments[0].click();",_)
        
        for sizeitem in self.size_chart:
            size = sizeitem['size']
            height_left,height_right = sizeitem["height"].split("-")
            weight_left,weight_right = sizeitem["weight"].split("-")
            
            self.finder.chain_find_element_safe(xpath=f"(//tbody/tr/td[text()='{size}']/following::input)[1]").parent.keyboard.chain_safe_input(InputValue=height_left)
            self.finder.chain_find_element_safe(xpath=f"(//tbody/tr/td[text()='{size}']/following::input)[2]").parent.keyboard.chain_safe_input(InputValue=height_right)
            self.finder.chain_find_element_safe(xpath=f"(//tbody/tr/td[text()='{size}']/following::input)[3]").parent.keyboard.chain_safe_input(InputValue=weight_left)
            self.finder.chain_find_element_safe(xpath=f"(//tbody/tr/td[text()='{size}']/following::input)[4]").parent.keyboard.chain_safe_input(InputValue=weight_right)
            
            
        
        return self